package alfred.mdp.utils;


import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;

/**
 */
public class Shutdowner {
    private static final ConcurrentMap<String, FuturesReference> futuresForGroup = new ConcurrentHashMap<String, FuturesReference>();
    public static final int DEFAULT_MAX_RETRIES = 5;
    public static final long DEFAULT_TIMEOUT = 500;
    public static final TimeUnit DEFAULT_TIMEUNIT = TimeUnit.MILLISECONDS;

    public static void configureGroup(String group, long timeout, TimeUnit timeUnit) {
        futuresForGroup.putIfAbsent(group, new FuturesReference(timeout, timeUnit));
    }

    public static void add(String group, Future futureTask) {
        futuresForGroup.putIfAbsent(group, new FuturesReference(DEFAULT_TIMEOUT, DEFAULT_TIMEUNIT));
        futuresForGroup.get(group).taskToWait.add(futureTask);
    }

    public static boolean shutdown(String group, int maxRetries) {
        while (!_shutdown(group)) {
            maxRetries--;
            if (maxRetries == 0) {
                return false;
            }
        }
        return true;
    }

    public static boolean shutdown(String group) {
        return shutdown(group, DEFAULT_MAX_RETRIES);
    }

    public static void shutdownCompleted(String group, Class caller) {
        futuresForGroup.get(group).shutdownWaiter.add(caller);
    }

    private static boolean _shutdown(String group) {
        boolean result = true;
        try {
            FuturesReference futuresReference = futuresForGroup.get(group);
            if (futuresReference != null) {
                Iterator<Future> iterator = futuresReference.taskToWait.iterator();
                while (iterator.hasNext()) {
                    Future nextFutureToCancel = iterator.next();
                    nextFutureToCancel.cancel(true);
                    Class shutdownedClass = futuresReference.shutdownWaiter.poll(futuresReference.timeout, futuresReference.timeUnit);
                    boolean isShutdownedClass = shutdownedClass != null;
                    if (isShutdownedClass) {
                        iterator.remove();
                    }
                    result &= isShutdownedClass;
                }
            } else {
                return false;
            }
        } catch (InterruptedException e) {
        }
        return result;
    }

    public final static class FuturesReference {
        private final BlockingQueue<Class> shutdownWaiter = new LinkedBlockingDeque<Class>();
        private final List<Future> taskToWait = Collections.synchronizedList(new LinkedList<Future>());
        private final long timeout;
        private final TimeUnit timeUnit;

        public FuturesReference(long timeout, TimeUnit timeUnit) {
            this.timeout = timeout;
            this.timeUnit = timeUnit;
        }
    }
}
